﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
                    
//using LumiSoft.Net.Mime.vCard;

namespace library
{
    partial class p2pFile
    {
        class Packet
        {
            p2pFile File;

            byte[] Address;

            string Filename;

            internal bool Arrived
            {
                get
                {
                    lock (dataArrivedObjectLocker)
                        return Arrives > 0;
                }
            }

            int Arrives;

            internal int RequestSent;

            internal bool MayHaveLocalData = true;

            object dataArrivedObjectLocker = new object();

            internal Packet(p2pFile file, byte[] address, string filename = null)
            {
                File = file;

                Address = address;

                Filename = filename;
            }

            internal void VerifyDataArrived(byte[] address, byte[] data)
            {
                if (!Addresses.Equals(Address, address))
                    return;

                //LocalPackets.OnPacketArrived -= VerifyDataArrived;

                ProcessDataArrived(data);
            }

            void ProcessDataArrived(byte[] data)
            {
                lock (dataArrivedObjectLocker)
                {
                   

                    File.ReturnRatio = (File.ReturnRatio + (RequestSent / (double)Arrives + 1)) / 2;

                    if (Arrives != 0)
                        return;


                    var packetType = (PacketTypes)data[0];

                    switch (packetType)
                    {
                        case PacketTypes.Addresses:

                            List<byte[]> addresses = Addresses.ToAddresses(data.Skip(Parameters.packetHeaderSize));

                            foreach (byte[] addr in addresses)
                                File.AddPacket(addr);

                            break;

                        case PacketTypes.Directory:

                            Directory.CreateDirectory(File.Filename);

                            Dictionary<byte[], string> files =
                                Addresses.ToDirectories(data.Skip(Parameters.packetHeaderSize).ToArray());

                            foreach (byte[] addr in files.Keys)
                                File.AddPacket(addr, Path.Combine(File.Filename, files[addr]));

                            break;

                        case PacketTypes.Content:

                            var buffer = data.Skip(Parameters.packetHeaderSize).ToArray();

                            var offset = BitConverter.ToInt32(data, 1);

                            DelayedWrite.Add(Filename ?? File.Filename, buffer, offset);

                            break;

                        case PacketTypes.Post:

                            DelayedWrite.Add(Filename, data, 0);

                            break;
                    }

                    Arrives++;
                }
            }

            internal void Get()
            {
                byte[] data = null;

                if (MayHaveLocalData)
                {
                    data = LocalPackets.Get(Address);

                    if (data == null)
                        MayHaveLocalData = false;
                }

                if (data == null)
                {
                    Client.Stats.PresumedReceived.Add((int)(Parameters.packetSize / File.ReturnRatio));

                    Client.Stats.belowMaxReceivedEvent.WaitOne(File.MayHaveLocalData()? 0 : Timeout.Infinite);

                    var sent = p2pRequest.Send(
                        address: Address,
                        wait: !File.MayHaveLocalData());

                    if (sent)
                    {
                        RequestSent++;

                        if(RequestSent == 1)
                            LocalPackets.OnPacketArrived += VerifyDataArrived;
                    }
                }
                else if (data != null)
                    ProcessDataArrived(data);
            }
        }
    }
}
